package com.gitee.wsl.serialization.id

import com.gitee.wsl.serialization.id.IntID.Companion.ID
import com.gitee.wsl.serialization.id.LongID.Companion.ID
import com.gitee.wsl.serialization.id.StringID.Companion.ID
import com.gitee.wsl.serialization.id.UIntID.Companion.ID
import com.gitee.wsl.serialization.id.ULongID.Companion.ID
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid


/**
 * [ID] 是用于表示 _唯一标识_ 的不可变类值类型。
 *
 * [ID] 是一个密封类型，它提供的最终类型有：
 *
 * - [StringID]
 * - [UUID]
 * - [IntID]
 * - [LongID]
 * - [UIntID]
 * - [ULongID]
 *
 * 它们可以粗略的被归类为字符串类型（ [UUID] 的字面值表现为字符串）和数字类型。
 *
 * ## 构造
 *
 * 在 Kotlin 中，直接使用各类型的伴生对象提供的扩展属性构建即可：
 *
 * ```Kotlin
 * 100.ID
 * 100L.ID
 * 100u.ID
 * "100".ID
 * UUID.random()
 * ```
 *
 * 在 Java 中，可以使用 `Identifies` 中提供的构造方法，它们通常被命名为 `of` 或以 `of` 为开头：
 *
 * ```Java
 * Identifies.of(100);
 * Identifies.of(100L);
 * Identifies.of("100");
 * Identifies.ofULong(100L);
 * Identifies.ofULong("100");
 * Identifies.uuid();
 * ```
 *
 * 也可以使用具体类型的伴生对象所提供的静态API：
 *
 * ```Java
 * IntID.valueOf(100);
 * LongID.valueOf(100L);
 * UIntID.valueOf(100);
 * ULongID.valueOf(100L);
 * StringID.valueOf("100");
 * UUID.random();
 * ```
 *
 * 这些伴生对象提供的静态API与 `Identifies` 中的内容相比缺少了一些辅助性的API，
 * 例如使用字符串构建无符号ID `ULongID`。
 *
 * ## 序列化
 *
 * 所有**具体的**ID类型都是可序列化的，它们都会通过 Kotlinx serialization
 * 提供一个可作为**字面值**序列化的序列化器实现。
 *
 * 所有的ID类型都**不会**被序列化为结构体，例如 [UIntID] 会被直接序列化为一个数字:
 *
 * ```kotlin
 * @Serializable
 * data class Foo(val value: UIntID)
 * // 序列化结果: {"value": 123456}
 * ```
 *
 * ## 顺序
 *
 * [ID] 实现 [Comparable] 并允许所有 [ID] 类型之间存在排序关系。
 * 具体的排序规则参考每个具体的 [ID] 类型的 [compareTo] 的文档说明。
 *
 * ## 字面值与 `toString`
 *
 * 一个 [ID] 所表示的字符串值即为其字面值，也就是 [ID.toString] 的输出结果。
 *
 * 对于 [StringID] 来说，字面值就是它内部的字符串的值。
 *
 * ```kotlin
 * val id = "abc".ID
 * // toString: abc
 * ```
 *
 * 对于 [UUID] 来说，字面值即为其内部 128 位数字通过一定算法计算而得到的具有规律且唯一的字符串值。
 *
 * ```kotlin
 * val id = UUID.random()
 * // toString: 817d2625-1c9b-4cc4-880e-5d6ba86a42b7
 * ```
 *
 * 对于各数字类型的ID [NumericalID] 来说，字面值即为数字转为字符串的值。
 *
 * ```kotlin
 * val iID: IntID = 1.ID     // toString: 1
 * val lID: LongID = 1L.ID   // toString: 1
 * val uiID: UIntID = 1u.ID  // toString: 1
 * val ul: ULong = 1u
 * val ulID: ULongID = ul.ID  // toString: 1
 * ```
 *
 * 在Java中需要尤其注意的是，一个相同的数值，
 * 使用无符号类型和有符号类型的ID在通过 `valueOf`
 * 构建的结果可能是不同的，获取到的 `value` 和字面值也可能是不同的。
 *
 * Java在操作无符号ID的时候需要注意使用相关的无符号API。
 * 以 `long` 为例：
 *
 * ```java
 * long value = -1;
 *
 * LongID longID = LongID.valueOf(value);
 * ULongID uLongID = ULongID.valueOf(value);
 *
 * System.out.println(longID);  // -1
 * System.out.println(uLongID); // 18446744073709551615
 *
 * System.out.println(longID.getValue());  // -1
 * System.out.println(uLongID.getValue()); // -1
 * ```
 *
 * 如果希望得到一些符合预期的结果，你应该使用Java中的无符号相关API：
 *
 * ```java
 * long value = Long.parseUnsignedLong("18446744073709551615");
 * ULongID uLongID = ULongID.valueOf(value);
 * System.out.println(uLongID); // 18446744073709551615
 * System.out.println(Long.toUnsignedString(uLongID.getValue()));
 * // 18446744073709551615
 * ```
 *
 * ## `equals` 与 `hashCode`
 *
 * [ID] 下所有类型均允许互相通过 [ID.equals] 判断是否具有相同的 **字面值**。
 * [ID.equals] 实际上不会判断类型，因此如果两个不同类型的 [ID] 的字面值相同，
 * 例如值为 `"1"` 的 [StringID] 和值为 `1` 的 [IntID]，它们之间使用 [ID.equals]
 * 会得到 `true`。
 *
 * [ID] 作为一个"唯一标识"载体，大多数情况下，它的类型无关紧要。
 * 并且 [ID] 属性的提供者也应当以抽象类 [ID] 类型本身对外提供，
 * 而将具体的类型与构建隐藏在实现内部。
 *
 * ```kotlin
 * public interface Foo {
 *    val id: ID
 * }
 *
 * internal class FooImpl(override val id: ULongID) : Foo
 * ```
 *
 * 如果你希望严格匹配两个 [ID] 类型，而不是将它们视为统一的"唯一标识"，那么使用 [ID.equalsExact]
 * 来进行。[equalsExact] 会像传统数据类型的 `equals` 一样，同时判断类型与值。
 *
 * 由于 [ID] 类型之间通过 [equals] 会仅比较字面值，且对外应仅暴露 [ID] 本身，
 * 但是不同类型但字面值相同的 [ID] 的 [hashCode] 值可能并不相同，
 * 因此 [ID] 这个抽象类型本身 **不适合** 作为一种 hash Key, 例如作为 HashMap 的 Key:
 *
 * ```kotlin
 * // ❌Bad!
 * val map1 = hashMapOf<ID, String>("1".ID to "value 1", 1.ID to "also value 1")
 * // size: 2, values: {1=value 1, 1=also value 1}
 * // ❌Bad!
 * val uuid = UUID.random()
 * val strId = uuid.toString().ID
 * val map2 = hashMapOf<ID, String>(uuid to "UUID value", strId to "string ID value")
 * // size: 2, values: {2afb3d3e-d3f4-4c15-89ed-eec0e258d533=UUID value, 2afb3d3e-d3f4-4c15-89ed-eec0e258d533=string ID value}
 * ```
 *
 * 如果有必要，你应该使用一个**具体的**最终ID类型作为某种 hash key, 例如：
 *
 * ```kotlin
 * // ✔ OK.
 * val map = hashMapOf<IntID, String>(1.ID to "value 1", 1.ID to "also value 1")
 * // size: 1, values: {1=also value 1}
 * ```
 *
 * @author ForteScarlet
 */
@Serializable(with = AsStringIDSerializer::class)
sealed class ID : Comparable<ID> {

    /**
     * ID 的 **字面值** 字符串。
     *
     * @return 字面值字符串
     */
    abstract override fun toString(): String

    /**
     * ID 的源值 hashcode，等于对应的源值的 hashcode。
     *
     * 不同类型但字面值相同的ID可能会有不同的 hashCode，例如字符串ID `"1"` 和数字ID `1`。
     *
     */
    abstract override fun hashCode(): Int

    /**
     * 判断另外一个 [ID] 是否与当前 [ID] **字面值相同**。
     *
     * 任意类型的 ID 的 [equals] 应始终可以与其他任意类型的 [ID] 进行字面值比对。
     * 例如一个字面值为字符串 `"1"` 的 [ID] 与字面值是数字 `1` 的 [ID] 通过 [equals]
     * 进行比对，那么结果将会是 `true`。
     *
     * 如果希望在比对的时候连带类型进行比对，参考使用 [equalsExact]。
     *
     * @see equalsExact
     */
    abstract override fun equals(other: Any?): Boolean

    /**
     * 判断另外一个 [ID] 是否与当前 [ID] **字面值与类型均相同**。
     *
     * 会同时比对类型与字面值，[equalsExact] 更类似于传统的 `equals` 逻辑。
     */
    public abstract fun equalsExact(other: Any?): Boolean

    /**
     * 复制一个当前ID。
     */
    public abstract fun copy(): ID
}


/**
 * 将ID视为字符串进行序列化/反序列化的序列化器。
 *
 * 会将任何 [ID] 序列化为 [String]；
 * 会将任何字符串反序列化为 [StringID]。
 *
 * 可以作为任意 [ID] 的通用序列化器。
 *
 */
public object AsStringIDSerializer : KSerializer<ID> {
    override fun deserialize(decoder: Decoder): StringID = decoder.decodeString().ID
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AsStringID", PrimitiveKind.STRING)
    override fun serialize(encoder: Encoder, value: ID) {
        encoder.encodeString(value.toString())
    }
}


/**
 * 通过一个**有符号**数字作为ID值的 [NumericalID] 实现。
 *
 * - [IntID] 32位有符号整型
 * - [LongID] 64位有符号整型
 *
 * 如果想要作为ID的数字已经超过64位有符号 ([LongID]) 所能表示的最大数字，
 * 那么建议使用其他类型来表示，例如 [ULongID] 或 [StringID]
 *
 *
 * @see IntID
 * @see LongID
 */
public sealed class SignedNumericID : NumericalID()

/**
 * 通过一个**无符号**数字作为ID值的 [NumericalID] 实现。
 *
 * - [UIntID] 32位无符号整型
 * - [ULongID] 64位无符号整型
 *
 * 如果想要作为ID的数字已经超过64位无符号 ([ULongID]) 所能表示的最大数字，
 * 那么建议使用其他类型来表示，例如 [StringID]
 *
 * 在 Java 中对无符号数字的操作需要有些注意的地方。
 * 具体描述请参考 [ID] 的文档注释中的相关说明。
 *
 * @see UIntID
 * @see ULongID
 */
sealed class UnsignedNumericID : NumericalID()


fun IntID.compareIntID(target: NumericalID): Int = when (target) {
    is IntID -> this.value.compareTo(target.value)
    is LongID -> this.value.compareTo(target.value)
    is UIntID -> when {
        this.value < 0 || target.value > NumericalID.INT_MAX_ON_UINT -> -1
        else -> this.value.compareTo(target.value.toInt())
    }

    is ULongID -> when {
        this.value < 0 || target.value > NumericalID.INT_MAX_ON_ULONG -> -1
        else -> this.value.compareTo(target.toInt())
    }
}

fun LongID.compareLongID(target: NumericalID): Int = when (target) {
    is IntID -> this.value.compareTo(target.value)
    is LongID -> this.value.compareTo(target.value)
    is UIntID -> this.value.compareTo(target.value.toLong())
    is ULongID -> when {
        this.value < 0L || target.value > NumericalID.LONG_MAX_ON_ULONG -> -1
        else -> this.value.compareTo(target.value.toLong())
    }
}

 fun UIntID.compareUIntID(target: NumericalID): Int = when (target) {
    is IntID -> when {
        target.value < 0 || this.value > NumericalID.INT_MAX_ON_UINT -> 1
        else -> this.value.toInt().compareTo(target.value)
    }

    is LongID -> this.value.toLong().compareTo(target.value)
    is UIntID -> this.value.compareTo(target.value)
    is ULongID -> this.value.compareTo(target.value)
}

fun ULongID.compareULongID(target: NumericalID): Int = when (target) {
    is IntID -> when {
        target.value < 0 || this.value > NumericalID.INT_MAX_ON_ULONG -> 1
        else -> this.value.toInt().compareTo(target.value)
    }

    is LongID -> when {
        target.value < 0 || this.value > NumericalID.LONG_MAX_ON_ULONG -> 1
        else -> this.value.toLong().compareTo(target.value)
    }

    is UIntID -> this.value.compareTo(target.value)
    is ULongID -> this.value.compareTo(target.value)
}

inline fun <reified T : ID> T.idCommonEq(other: Any?, orElse: T.(ID) -> Boolean): Boolean {
    if (other == null) return false
    if (other === this) return true
    if (other !is ID) return false

    return orElse(other)
}

inline fun <reified T : ID> T.idExactEq(other: Any?, orElse: T.(T) -> Boolean): Boolean {
    if (other == null) return false
    if (other === this) return true
    if (other !is T) return false

    return orElse(other)
}

/**
 * 同 [ID.toString], 得到 [ID] 的字面值。
 *
 */
public inline val ID.literal: String
    get() = toString()

/**
 * 尝试将 [this] 转化为 [Int]。
 * 如果为 [NumericalID] 则直接使用 [NumericalID.toInt],
 * 否则使用 [notNumerical] 转化。默认会尝试使用 [literal.toIntOrNull()][String.toIntOrNull]。
 */
@JvmOverloads
@JvmName("toIntOrNull")
public inline fun ID.toIntOrNull(notNumerical: ID.() -> Int? = { literal.toIntOrNull() }): Int? =
    (this as? NumericalID)?.toInt() ?: notNumerical()

/**
 * 尝试将 [this] 转化为 [Int]。
 * 如果为 [NumericalID] 则直接使用 [NumericalID.toInt],
 * 否则使用 [notNumerical] 转化。默认会尝试使用 [literal.toInt()][String.toInt]。
 */
@JvmOverloads
@JvmName("toInt")
public inline fun ID.toInt(notNumerical: ID.() -> Int = { literal.toInt() }): Int =
    (this as? NumericalID)?.toInt() ?: notNumerical()

/**
 * 尝试将 [this] 转化为 [Long]。
 * 如果为 [NumericalID] 则直接使用 [NumericalID.toLong],
 * 否则使用 [notNumerical] 转化。默认会尝试使用 [literal.toLongOrNull()][String.toLongOrNull]。
 */
@JvmOverloads
@JvmName("toLongOrNull")
public inline fun ID.toLongOrNull(notNumerical: ID.() -> Long? = { literal.toLongOrNull() }): Long? =
    (this as? NumericalID)?.toLong() ?: notNumerical()

/**
 * 尝试将 [this] 转化为 [Long]。
 * 如果为 [NumericalID] 则直接使用 [NumericalID.toLong],
 * 否则使用 [notNumerical] 转化。默认会尝试使用 [literal.toLong()][String.toLong]。
 */
@JvmOverloads
@JvmName("toLong")
public inline fun ID.toLong(notNumerical: ID.() -> Long = { literal.toLong() }): Long =
    (this as? NumericalID)?.toLong() ?: notNumerical()

/**
 * 尝试将 [this] 转化为 [UInt]。
 * 如果为 [NumericalID] 则直接使用 [NumericalID.toInt].[toUInt][Int.toUInt],
 * 否则使用 [notNumerical] 转化。默认会尝试使用 [literal.toUIntOrNull()][String.toIntOrNull]。
 */
@JvmOverloads
@JvmName("toUIntOrNull")
public inline fun ID.toUIntOrNull(notNumerical: ID.() -> UInt? = { literal.toUIntOrNull() }): UInt? =
    (this as? NumericalID)?.toInt()?.toUInt() ?: notNumerical()

/**
 * 尝试将 [this] 转化为 [UInt]。
 * 如果为 [NumericalID] 则直接使用 [NumericalID.toInt].[toUInt][Int.toUInt],
 * 否则使用 [notNumerical] 转化。默认会尝试使用 [literal.toUInt()][String.toInt]。
 */
@JvmOverloads
@JvmName("toUInt")
public inline fun ID.toUInt(notNumerical: ID.() -> UInt = { literal.toUInt() }): UInt =
    (this as? NumericalID)?.toInt()?.toUInt() ?: notNumerical()

/**
 * 尝试将 [this] 转化为 [ULong]。
 * 如果为 [NumericalID] 则直接使用 [NumericalID.toLong].[toULong][Long.toULong],
 * 否则使用 [notNumerical] 转化。默认会尝试使用 [literal.toULongOrNull()][String.toLongOrNull]。
 */
@JvmOverloads
@JvmName("toULongOrNull")
public inline fun ID.toULongOrNull(notNumerical: ID.() -> ULong? = { literal.toULongOrNull() }): ULong? =
    (this as? NumericalID)?.toLong()?.toULong() ?: notNumerical()

/**
 * 尝试将 [this] 转化为 [ULong]。
 * 如果为 [NumericalID] 则直接使用 [NumericalID.toLong].[toULong][Long.toULong],
 * 否则使用 [notNumerical] 转化。默认会尝试使用 [literal.toULong()][String.toLong]。
 */
@JvmOverloads
@JvmName("toULong")
public inline fun ID.toULong(notNumerical: ID.() -> ULong = { literal.toULong() }): ULong =
    (this as? NumericalID)?.toLong()?.toULong() ?: notNumerical()

/**
 * 尝试将 [this] 转为 [IntID] 类型。
 * 如果不是数字ID，则会使用 [notNumerical] 获取结果。默认使用 [String.toInt]。
 */
@JvmOverloads
@JvmName("toIntID")
public inline fun ID.toIntID(notNumerical: ID.() -> IntID = { literal.toInt().ID }): IntID =
    this as? IntID ?: (this as? NumericalID)?.toInt()?.ID ?: notNumerical()

/**
 * 尝试将 [this] 转为 [UIntID] 类型。
 * 如果不是数字ID，则会使用 [notNumerical] 获取结果。默认使用 [String.toUInt]。
 */
@JvmOverloads
@JvmName("toUIntID")
public inline fun ID.toUIntID(notNumerical: ID.() -> UIntID = { literal.toUInt().ID }): UIntID =
    this as? UIntID ?: (this as? NumericalID)?.toUInt()?.ID ?: notNumerical()

/**
 * 尝试将 [this] 转为 [LongID] 类型。
 * 如果不是数字ID，则会使用 [notNumerical] 获取结果。默认使用 [String.toLong]。
 */
@JvmOverloads
@JvmName("toLongID")
public inline fun ID.toLongID(notNumerical: ID.() -> LongID = { literal.toLong().ID }): LongID =
    this as? LongID ?: (this as? NumericalID)?.toLong()?.ID ?: notNumerical()

/**
 * 尝试将 [this] 转为 [ULongID] 类型。
 * 如果不是数字ID，则会使用 [notNumerical] 获取结果。默认使用 [String.toULong]。
 */
@JvmOverloads
@JvmName("toULongID")
public inline fun ID.toULongID(notNumerical: ID.() -> ULongID = { literal.toULong().ID }): ULongID =
    this as? ULongID ?: (this as? NumericalID)?.toULong()?.ID ?: notNumerical()


/*// UUIDs

*//**
 * 通过一个 [kotlin.uuid.Uuid] 构建 [UUID].
 *
 * @since 4.6.1
 *//*
@ExperimentalUuidApi
@get:JvmName("uuid")
public val Uuid.ID: UUID
    get() = toLongs { mostSignificantBits, leastSignificantBits ->
        UUID.from(mostSignificantBits, leastSignificantBits)
    }

*//**
 * 将一个 [UUID] 转化为 [kotlin.uuid.Uuid] .
 *
 * @since 4.6.1
 *//*
@ExperimentalUuidApi
public fun UUID.toKotlin(): Uuid =
    Uuid.fromLongs(mostSignificantBits, leastSignificantBits)*/
